package org.dreamwork.plugins.network.assistant.gui

import com.intellij.ui.JBColor
import org.dreamwork.ui.concurrent.Handler
import org.dreamwork.ui.concurrent.IMessageHandler
import org.dreamwork.ui.concurrent.Message
import org.dreamwork.util.Tools
import java.awt.Color
import java.awt.Component
import java.awt.Graphics
import java.io.IOException
import java.io.OutputStream
import java.text.SimpleDateFormat
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue
import javax.swing.ImageIcon
import javax.swing.JTextPane
import javax.swing.text.AttributeSet
import javax.swing.text.Style
import javax.swing.text.StyleConstants
import javax.swing.text.StyledDocument

class CircleIcon : ImageIcon() {
    var state = 0

    @Synchronized
    override fun paintIcon(c: Component, g: Graphics, x: Int, y: Int) {
        g.clearRect(x, y, EDGE, EDGE)
        val color = if (state == 0) Color.GRAY else Color.GREEN
        g.color = color
        g.fillArc(x, y, EDGE, EDGE, 0, 360)
    }

    override fun getIconWidth(): Int {
        return EDGE
    }

    override fun getIconHeight(): Int {
        return EDGE
    }

    companion object {
        private const val EDGE = 16
    }
}

class TextAreaLogger internal constructor(private val text: JTextPane, private var showTime: Boolean, private var showAscii: Boolean) : IMessageLogger, IMessageHandler, Runnable {
    private var autoReturn = true
    private val handler: Handler = Handler(this)
    private val queue: BlockingQueue<Any> = ArrayBlockingQueue(16)
    private val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
    private val doc: StyledDocument = text.styledDocument
    private var out: OutputStream? = null
    private val normal: Style
    private val info: Style
    private val error: Style

    private class MessageWrapper internal constructor(var message: ByteArray?, var level: Int)

    override fun setShowTime(showTime: Boolean) {
        this.showTime = showTime
    }

    override fun setShowAscii(showAscii: Boolean) {
        this.showAscii = showAscii
    }

    override fun setAutoReturn(autoReturn: Boolean) {
        this.autoReturn = autoReturn
    }

    override fun append(message: ByteArray?) {
        append(message, IMessageLogger.NORMAL)
    }

    override fun append(message: ByteArray?, level: Int) {
        queue.offer(MessageWrapper(message, level))
    }

    override fun dumpTo(out: OutputStream?) {
        if (out == null) {
            // cancel dump to file
            if (this.out != null) try {
                this.out!!.close()
            } catch (ex: IOException) {
                ex.printStackTrace()
            }
        }
        this.out = out
    }

    override fun handleUIMessage(message: Message) {
        var line: String
        val buff = message.obj as ByteArray
        if (buff.isEmpty()) {
            return
        }
        line = if (showAscii || message.what != IMessageLogger.NORMAL) {
            String(buff)
        } else {
            Tools.toHex(buff)
        }
        if (showTime) {
            line = String.format("[%s] %s", sdf.format(System.currentTimeMillis()), line)
        }
        val `as`: AttributeSet
        `as` = when (message.what) {
            IMessageLogger.NORMAL -> normal
            IMessageLogger.INFO -> info
            IMessageLogger.ERROR -> error
            else -> normal
        }
        //        text.append (line);
        try {
            doc.insertString(doc.length, line, `as`)
            if (autoReturn) doc.insertString(doc.length, CRLF, `as`)
        } catch (ex: Exception) {
            // error
            ex.printStackTrace()
        }
        text.caretPosition = text.document.length
        if (out != null) try {
            out!!.write(line.toByteArray())
            out!!.write(CRLF.toByteArray())
            out!!.flush()
        } catch (ex: IOException) {
            // ignore
        }
    }

    override fun handleNonUIMessage(message: Message) {}
    override fun run() {
        while (true) {
            try {
                val o = queue.take()
                if (o === BREAK) {
                    break
                }
                val wrapper = o as MessageWrapper
                handler.sendUIMessage(wrapper.level, wrapper.message)
            } catch (ex: InterruptedException) {
                ex.printStackTrace()
            }
        }
        println("the logger looper bread")
    }

    companion object {
        private val BREAK: Any = ByteArray(0)
        private const val CRLF = "\r\n"
    }

    init {
        normal = doc.addStyle("normal", null)
        StyleConstants.setForeground(normal, JBColor.namedColor("Panel.foreground", 0xe5e5e5)/*Color.decode ("#e5e5e5")*/)
        StyleConstants.setFontFamily(normal, "Consolas")
        info = doc.addStyle("info", normal)
        StyleConstants.setForeground(info, Color.decode("#666600"))
        error = doc.addStyle("error", normal)
        StyleConstants.setForeground(error, JBColor.RED)
    }
}